package com.cosytek.farmmanager;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
import android.util.Log;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.util.ArrayList;

/**
 * Created by Happy on 2015/3/26.
 */
public class MsgManager implements ServerRequest.IServerRequestListener {

    public class Msg {
        private final int mId;
        private JSONObject mJsonObject;
        private int mFlag;
        private String mTitle;
        private String mAbstract;

        public Msg(int id, String text, int flag) {
            this.mId = id;
            try {
                mJsonObject = new JSONObject(text);
                mTitle = mJsonObject.getString("title");
                mAbstract = mJsonObject.getString("abstract");
            } catch (JSONException e) {
                e.printStackTrace();
            }
            this.mFlag = flag;
        }

        public int id() {
            return this.mId;
        }

        public String title() {return this.mTitle;}

        public String abstracts() {return this.mAbstract;}

        public int flag() {
            return this.mFlag;
        }

        public void setFlag(int i) {
            mFlag = i;
        }
    }

    private static final MsgManager instance = new MsgManager();
    private static final String TAG = "MsgManager";
    private static final String SQL_CREATE_MESSAGE_TABLE = "CREATE TABLE IF NOT EXISTS %s " +
            "(MsgID INTEGER PRIMARY KEY, MsgText TEXT, Flag INTEGER)";
    private static final String SQL_MESSAGE_TABLE = "msg";
    private static final String SQL_READ_MESSAGE = "SELECT * FROM %s LIMIT %d OFFSET %d";
    private static final String SQL_UPDATE_MESSAGE_FLAG = "UPDATE %s SET FLAG=%d WHERE MsgID=%d";
    private static final String SQL_WRITE_MESSAGE = "INSERT OR REPLACE INTO %s VALUES (?, ?, ?)";
    private static final String SQL_DB_FILE_NAME = "Msg.db";
    private static final String SQL_DB_FILE_PATH = "/cosytek/fm/";
    private static final String SQL_COUNT_ALL = "SELECT Count(*) FROM %s";
    private static final String SQL_COUNT_UNREAD = "SELECT Count(*) FROM %s WHERE Flag=0";
    private static final String SQL_TRIM_TABLE = "DELETE FROM %s LIMIT %d OFFSET %d";
    private static final String MSG_URL_PATH = "/loadMsg";
    private static final int MAX_STORE_MSG_COUNT = 500;
    private static final String MSG_SET_UNREAD_URL_PATH = "/setReadMsg?id=%d";
    private static final String REQUEST_TAG_LOAD_MSG = "load";
    private static final String REQUEST_TAG_SET_READ = "set";

    private String mToken;

    public static MsgManager instance() {
        return instance;
    }

    public void onMsgReceived(String raw) {
        parseJSON(raw);
    }

    /**
     * JSON listener.
     * @author Happy
     *
     */
    public interface NotifyListener {
        /**
         * JSON string.
         * @param jsonObject json object
         */
        void onJSONNotify(final int msdId, final String type, final JSONObject jsonObject);
    }

    /**
     * list.
     */
    private ArrayList<NotifyListener> mListenerList = new ArrayList<NotifyListener>();

    /**
     * get list.
     * @return list
     */
    public ArrayList<NotifyListener> getList() {
        return mListenerList;
    }
    /**
     * add listener.
     * @param listener listener
     */
    public void addListener(NotifyListener listener) {
        if (isListenerExist(listener)) {
            return;
        }

        mListenerList.add(listener);
    }

    private boolean isListenerExist(NotifyListener listener) {
        for(NotifyListener listener1 : mListenerList) {
            if (listener == listener1) {
                return true;
            }
        }
        return false;
    }
    /**
     * remove listener.
     * @param listener listener
     */
    public void removeListener(NotifyListener listener) {
        mListenerList.remove(listener);
    }

    /**
     * Parser jsonString.
     * @param jsonStr
     * JsonString.
     */
    private void parseJSON(final String jsonStr) {
        if (null == jsonStr || jsonStr.equals("")) {
            return;
        }

        String type = null;

        try {
            JSONObject jsonObj = new JSONObject(jsonStr);
            if (!jsonObj.has("id") || !jsonObj.has("type")) {
                // invalid message
                return;
            }

            // write to database
            int msgId = jsonObj.getInt("id");
            Log.d(TAG, String.format("save new msg, id:%d", msgId));
            writeUnreadMsg(msgId, jsonStr);

            type = jsonObj.getString("type");

            for (NotifyListener ll : mListenerList) {
                ll.onJSONNotify(msgId, type, jsonObj);
            }

        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

    public Msg loadMessage(int offset, int limit) {
        return loadMessage(SQL_MESSAGE_TABLE, offset, limit);
    }

    private Msg loadMessage(String table, int offset, int limit) {
        SQLiteDatabase db = openMsgDB();
        try {
            Cursor c = db.rawQuery(String.format(SQL_READ_MESSAGE, table, limit, offset), null);
            if (c == null) {
                return null;
            }
            c.moveToFirst();
            int msgId = c.getInt(0);
            String msgText = c.getString(1);
            int flag = c.getInt(2);
            c.close();
            return new Msg(msgId, msgText, flag);
        } finally {
            if (db != null) {
                db.close();
            }
        }
    }

    public void markMsgRead(int msgId) {
        SQLiteDatabase db = openMsgDB();
        try {
            db.execSQL(String.format(SQL_UPDATE_MESSAGE_FLAG, SQL_MESSAGE_TABLE, 1, msgId));

            markMsgReadAtServer(msgId);
        } finally {
            if (db != null) {
                db.close();
            }
        }
    }

    private void markMsgReadAtServer(int msgId) {
        ServerRequest r = ServerRequest.newPostRequest(this, mToken, String.format(MSG_SET_UNREAD_URL_PATH, msgId));
        r.setTag(REQUEST_TAG_SET_READ);
        r.execute();
    }

    public void writeUnreadMsg(int msgId, String msgText) {
        SQLiteDatabase db = openMsgDB();
        try {
            String insert = String.format(SQL_WRITE_MESSAGE, SQL_MESSAGE_TABLE);
            db.execSQL(insert, new Object[]{msgId, msgText, 0});
        } finally {
            if (db != null) {
                db.close();
            }
        }
    }

    public int getUnreadMsgCount() {
        SQLiteDatabase db = openMsgDB();
        try {
            Cursor c = db.rawQuery(String.format(SQL_COUNT_UNREAD, SQL_MESSAGE_TABLE), null);
            c.moveToFirst();
            int count = c.getInt(0);
            c.close();
            return count;
        } finally {
            if (db != null) {
                db.close();
            }
        }
    }

    public int getTotalMsgCount() {
        SQLiteDatabase db = openMsgDB();
        try {
            Cursor c = db.rawQuery(String.format(SQL_COUNT_ALL, SQL_MESSAGE_TABLE), null);
            c.moveToFirst();
            int count = c.getInt(0);
            c.close();
            return count;
        } finally {
            if (db != null) {
                db.close();
            }
        }
    }

    public void TrimMsgTableIfNeed() {
        int total = getTotalMsgCount();
        if (total <= MAX_STORE_MSG_COUNT) {
            return;
        }

        int delete = total - MAX_STORE_MSG_COUNT;
        SQLiteDatabase db = openMsgDB();
        try {
            db.execSQL(String.format(SQL_TRIM_TABLE, SQL_MESSAGE_TABLE, delete, MAX_STORE_MSG_COUNT));
        } finally {
            if (db != null) {
                db.close();
            }
        }
    }

    private SQLiteDatabase openMsgDB() {
        File externalSD = Environment.getExternalStorageDirectory();
        File dbPath = new File(externalSD, SQL_DB_FILE_PATH);
        if (!dbPath.exists()) {
            dbPath.mkdirs();
        }

        File dbFile = new File(dbPath, SQL_DB_FILE_NAME);

        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbFile, null);
        db.execSQL(String.format(SQL_CREATE_MESSAGE_TABLE, SQL_MESSAGE_TABLE));

        return db;
    }

    public void loadUnreadMsgFromServer(String token) {
        mToken = token;
        ServerRequest r = ServerRequest.newGetRequest(this, mToken, MSG_URL_PATH);
        r.setTag(REQUEST_TAG_LOAD_MSG);
        r.execute();
    }

    @Override
    public void onRequestCompleted(ServerRequest r, boolean success, ServerError error, String respBody) {
        String tag = r.tag();
        switch (tag) {
            case REQUEST_TAG_LOAD_MSG:
                if (success) {
                    onLoadMsgFromServerCompleted(respBody);
                } else {
                    Log.e(TAG, error.getMsg());
                }
                break;
            case REQUEST_TAG_SET_READ:
                break;
        }
    }

    private void onLoadMsgFromServerCompleted(String respBody) {
        try {
            JSONArray strArray = new JSONArray(respBody);
            for (int i = 0; i < strArray.length(); ++i) {
                String msg = strArray.getString(i);
                onMsgReceived(msg);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

}
